﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace IP2Client
{
    enum AccessRights { ERROR = 0, USER = 1, DOCTOR = 9 }

    public partial class Form1 : Form
    {
        private bool handleUpdates;
        private bool bTrainingIsStarted;
        private DataHandler dataHandler;
        private SerialPort serialPort;

        private bool increasingPower;
        private DateTime startTime;

        public Form1()
        {
            dataHandler = new DataHandler(applicationTimer);
            handleUpdates = true;
            bTrainingIsStarted = false;

            serialPort = new SerialPort("COM...");
            serialPort.ReadTimeout = serialPort.WriteTimeout = 500;

            string[] portNames = SerialPort.GetPortNames();

            InitializeComponent();

            if (portNames.Length != 0)
            {
                cbComPorts.DataSource = sortPortNames(portNames);
                cbComPorts.DropDownStyle = ComboBoxStyle.DropDownList;
            }

            cbComPorts.Enabled = false;
        }

        private void sendText()
        {
            string textToSend = sendMessageBox.Text;

            if (textToSend != string.Empty)
            {
                ConnectionHandler.SendObject("!broadcast " + textToSend);

                string timeText = DateTime.Now.ToLocalTime().ToLongTimeString();

                chatBox.SelectionFont = new Font(DefaultFont, FontStyle.Bold);
                chatBox.AppendText(timeText);

                chatBox.SelectionFont = new Font(DefaultFont, FontStyle.Regular);
                chatBox.AppendText(" YOU: " + textToSend + "\n");
                sendMessageBox.Text = string.Empty;
            }
        }

        private List<String> sortPortNames(string[] portNames)
        {
            var tmpSortedPortNames = portNames.OrderBy(port => Convert.ToInt32(port.Replace("COM", string.Empty)));

            List<string> sortedPortNames = new List<String>();
            foreach (string str in tmpSortedPortNames)
            {
                sortedPortNames.Add(str);
            }

            return sortedPortNames;
        }

        /// <summary>
        ///  Method for reading incoming messages
        /// </summary>
        private string ReadMessage()
        {
            object rcvdObject = ConnectionHandler.ReadObject();

            string sRcvdText = rcvdObject as string;

            if (sRcvdText != null)
            {
                return sRcvdText;
            }

            return string.Empty;
        }

        private void sendMessageBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Enter)
            {
                sendText();
                e.Handled = true;
            }
        }

        private void sendMessageButton_Click(object sender, EventArgs e)
        {
            sendText();
        }

        private void loginButton_Click(object sender, EventArgs e)
        {
            if (ConnectionHandler.HandledClient != null)
            {
                if (ConnectionHandler.HandledClient.Connected)
                {
                    ConnectionHandler.SendObject("!disconnect");

                    ConnectionHandler.ClientSSLStream.Dispose();
                    ConnectionHandler.HandledClient.Close();
                    ConnectionHandler.HandledClient = null;

                    tbIPAddress.Enabled = tbPassword.Enabled = tbUsername.Enabled = true;
                    loginButton.Text = "Connect";
                    manageToolStripMenuItem.Visible = false;
                    btnStartTraining.Enabled = false;
                    btnStartTraining.Visible = false;
                }
            }
            else
            {
                AccessRights returnedAccessRights = login();

                if (returnedAccessRights == AccessRights.ERROR)
                    MessageBox.Show("Error logging in..");

                if (returnedAccessRights == AccessRights.DOCTOR || returnedAccessRights == AccessRights.USER)
                {
                    tbIPAddress.Enabled = tbPassword.Enabled = tbUsername.Enabled = false;
                    loginButton.Text = "Disconnect";

                    if (returnedAccessRights == AccessRights.DOCTOR)
                    {
                        manageToolStripMenuItem.Visible = true;
                        cbComPorts.Enabled = false;
                        btnStartTraining.Enabled = false;
                        btnStartTraining.Visible = false;
                    }
                    else if (returnedAccessRights == AccessRights.USER)
                    {
                        cbComPorts.Enabled = true;
                        btnStartTraining.Enabled = true;
                        btnStartTraining.Visible = true;
                    }
                }
            }
        }

        private AccessRights login()
        {
            string inputIPAddress = tbIPAddress.Text;
            IPAddress outputIPAddress = null;

            bool ipIsOk = IPAddress.TryParse(inputIPAddress, out outputIPAddress);

            if (!ipIsOk)
            {
                return AccessRights.ERROR;
            }

            ConnectionHandler.HandledClient = new TcpClient();

            try
            {
                ConnectionHandler.HandledClient.Connect(outputIPAddress, 1338 /* 1337++ */);
            }
            catch (Exception) { return AccessRights.ERROR; }

            if (!ConnectionHandler.HandledClient.Connected)
                return AccessRights.ERROR;

            ConnectionHandler.SetupSSL(inputIPAddress);

            string sStringToSend = tbUsername.Text + " " + ClientSecurity.CalculateMD5Hash(tbPassword.Text);
            ConnectionHandler.SendObject(sStringToSend);

            string queryReturn = ReadMessage();

            switch (queryReturn)
            {
                case "PATIENT": return AccessRights.USER;
                case "DOCTOR": return AccessRights.DOCTOR;
                default: return AccessRights.ERROR;
            }
        }

        private void applicationTimer_Tick(object sender, EventArgs e)
        {
            //Update inclusief ophalen van gegevens
            if (handleUpdates)
                handleTcpClientUpdates();

            //Update de weergave van alle gegevens
            updateVisual();
        }

        private void handleTcpClientUpdates()
        {
            if (bTrainingIsStarted)
            {
                dataHandler.HandleInput(serialPort);
                updateTraining();
            }

            if (ConnectionHandler.HandledClient == null)
            {
                return;
            }

            if (ConnectionHandler.HandledClient.Connected)
            {
                //Data aftroggelen bij de client.
                if (ConnectionHandler.HandledClient.Available > 0)
                {
                    object rcvdObject = ConnectionHandler.ReadObject();

                    //checked typecasten is leuk maar dit is toch iets efficienter.
                    //Is het en meting? lolnope want dat schijnt het nooit te zijn.
                    if (rcvdObject is Measurement)
                    {
                        Measurement rcvdMeasurement = rcvdObject as Measurement;
                    }
                    //haha, het is dus een string! oh magic.
                    //afhandelen die string.
                    else if (rcvdObject is string)
                    {
                        string sRcvdText = rcvdObject as string;

                        if (sRcvdText != string.Empty)
                        {
                            string timeText = DateTime.Now.ToLocalTime().ToLongTimeString();

                            chatBox.SelectionFont = new Font(DefaultFont, FontStyle.Bold);
                            chatBox.AppendText(timeText);

                            chatBox.SelectionFont = new Font(DefaultFont, FontStyle.Regular);
                            chatBox.AppendText(" " + sRcvdText + "\n");
                        }
                    }
                }
            }
        }

        private void updateVisual()
        {
            Measurement laatsteMeting = null;

            if(dataHandler.Measurements.Count > 0)
                //Probeer de laatste meting weg te schrijven naar laatsteMeting
                //Kan een Exception opgooien als de lijst leeg is aan het begin,
                //Maar dit zou niet mogen gebeuren!
                laatsteMeting = dataHandler.Measurements.Last();

            if (laatsteMeting != null)
            {
                lblSetPower.Text = Convert.ToString(laatsteMeting.Power);
                lblCurrentPower.Text = Convert.ToString(laatsteMeting.CurrentPower);
                lblCurrentSpeed.Text = Convert.ToString(laatsteMeting.Speed);
                lblCurrentHeartrate.Text = Convert.ToString(laatsteMeting.HeartBeats);
                lblTrainingTime.Text = Convert.ToString(laatsteMeting.Time);
                lblCurrentDistance.Text = Convert.ToString(laatsteMeting.Distance);
                lblCurrentRPM.Text = Convert.ToString(laatsteMeting.Rpm);
                lblCurrentBurnedEnergy.Text = Convert.ToString(laatsteMeting.Energy);
            }

            if (bTrainingIsStarted)
            {
                btnStartTraining.Text = "Stop Training";
                //cbComPorts.Enabled = false;
            }
            else
            {
                btnStartTraining.Text = "Start Training";
                //cbComPorts.Enabled = true;
            }
        }

        private void cbComPorts_SelectedIndexChanged(object sender, EventArgs e)
        {
            string selectedCom = cbComPorts.SelectedItem.ToString();
            serialPort.PortName = selectedCom;
        }

        private void btnStartTraining_Click(object sender, EventArgs e)
        {
            startTime = DateTime.Now;
            increasingPower = true;

            //Alvast even kijken of de poort geopend is, anders gaan we hem sluiten.
            if (!bTrainingIsStarted)
            {
                try
                {
                    cbComPorts.Enabled = false;
                    openOrCloseConnection(true);
                    bTrainingIsStarted = true;
                    //trainingTimer.Start();
                }
                catch (Exception ex)
                {
                    serialPort.Close();
                    MessageBox.Show(ex.Message, "Fout!");
                }
            }
            else
            {
                //Sluiten die handel
                openOrCloseConnection(false);
                cbComPorts.Enabled = true;
                bTrainingIsStarted = false;
                //trainingTimer.Stop();
                sendMeasurementsToServer();
            }

            updateVisual();
        }

        private void sendMeasurementsToServer()
        {
            ConnectionHandler.SendObject(dataHandler.Measurements);
        }

        public void openOrCloseConnection(bool open)
        {
            if (open && !serialPort.IsOpen)
            {
                serialPort.Open();

                serialPort.WriteLine("cm");
                serialPort.ReadLine();

                serialPort.WriteLine("id");
                string returnedId = serialPort.ReadLine();

                //Magere check om te kijken of de string de waarde bevat die we ook daadwerkelijk willen weten
                if (!returnedId.StartsWith("ACK") && !returnedId.StartsWith("ERROR"))
                {
                    this.Text = "IP2 - " + returnedId;
                }
                else
                {
                    this.Text = "IP2 - Fout bij ophalen ID";
                }
            }
            else if (!open && serialPort.IsOpen)
            {
                serialPort.Close();
            }
        }

        private void patientHistoryToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Form historyPatientSelectForm = new PatientHistorySelect();
            historyPatientSelectForm.ShowDialog();
            historyPatientSelectForm.Focus();
        }

        private void addUserToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Form addUserForm = new UserManagement();
            addUserForm.ShowDialog();
            addUserForm.Focus();
        }

        /// <summary>
        /// This method is pretty stupid but it seems to work
        /// Not my greatest work.
        /// </summary>
        /// <author>Tim Cocu</author>
        private void updateTraining()
        {
            int iCurrentPower = dataHandler.Measurements.Last().CurrentPower;

            if (startTime.AddMinutes(10).CompareTo(DateTime.Now) > 0)
            {
                if (iCurrentPower < 400 && increasingPower)
                    serialPort.WriteLine("PW " + (iCurrentPower += 5));
                else if (iCurrentPower  > 25 && !increasingPower)
                    serialPort.WriteLine("PW " + (iCurrentPower -= 5));
                else if (iCurrentPower >= 400 && increasingPower)
                    increasingPower = false;
                else if (iCurrentPower <= 25 && !increasingPower)
                    increasingPower = true;
            }
            else
            {
                bTrainingIsStarted = false;
                MessageBox.Show("Gestart: " + startTime + "Gestopt: " + DateTime.Now);
                MessageBox.Show("Training ended!");
                return;
            }
        }
    }
}
